#ifndef _FPGA_COMMON_H_
#define _FPGA_COMMON_H_

#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
// autoconfig.h最在Makefile里这样写 “gcc  xx.c --include autoconfig.h ”
#include <autoconfig.h>

// #include "fpga_data_build.h"


#define MIN_RX_PACK_SIZE (38)  // debug
extern const char *dev_desc;
#define MAGIC_FPGA_IF "im_fpga_if"
extern char *pmagic;

#if defined(CONFIG_USE_DEBUG_ENV)
#define ENV_PRINT_E(fmt, ...)                              \
    if (env_print_e) {                                     \
	_ctstprint(C_ERROR, C_NORMAL, fmt, ##__VA_ARGS__); \
    }
#define ENV_PRINT_W(fmt, ...)                             \
    if (env_print_w) {                                    \
	_ctstprint(C_WARN, C_NORMAL, fmt, ##__VA_ARGS__); \
    }
#else
#define ENV_PRINT_E(fmt, ...)
#define ENV_PRINT_W(fmt, ...)
#endif


#if defined(CONFIG_FACTORY_FOR_TR600)
struct fpga_dev
{
    int fd;  ///< 设备FD，正常 fpga_open 后返回
    // unsigned long pid;  ///< 设备id，正常 fpga_open 后返回
    struct fpga_if *interface;  // 只能放在这个原来没用的变量位置，否则结构体长度变化，不可预知崩溃
    char           *desc;       ///< 设备描述，，正常 fpga_open 后返回
    // unsigned long   checksum;   ///< OTDR
    ///< 曲线数据校验值，fpga_otdr_isfinish返回
    unsigned long cum_count;  ///< 累加次数
    unsigned long mask_amp;   ///< amp
                              ///< 有效值掩码，fpga_test_param.hw_amp的取值范围
    unsigned long mask_apd;   ///< apd
                              ///< 有效值掩码，fpga_test_param.hw_apd_vol的取值范围
    pthread_mutex_t mutex;    ///< 互斥锁
    // void *private;

    // 如下内容放入private
    unsigned long sample_freq;  ///< 采样频率选择，单位hz
    unsigned long adopt_pt;     ///< 采样点数，单位个
    unsigned char ch;

    unsigned long checksum;  ///< OTDR
                             ///< 曲线数据校验值，fpga_otdr_isfinish返回
                             // char *        otdr_cache;
};

#elif defined(CONFIG_FACTORY_FOR_TR700)

struct fpga_dev
{
    int fd;  ///< 设备FD，正常 fpga_open 后返回
    // unsigned long pid;			///< 设备id，正常 fpga_open 后返回
    struct fpga_if *interface;  // 只能放在这个原来没用的变量位置，否则结构体长度变化，不可预知崩溃
    char           *desc;       ///< 设备描述，，正常 fpga_open 后返回
    unsigned long   checksum;   ///< OTDR 曲线数据校验值，fpga_otdr_isfinish返回
    unsigned long   cum_count;  ///< 累加次数
    unsigned long   mask_amp;   ///< amp 有效值掩码，fpga_test_param.hw_amp的取值范围
    unsigned long   mask_apd;   ///< apd 有效值掩码，fpga_test_param.hw_apd_vol的取值范围
    pthread_mutex_t mutex;      ///< 互斥锁
    // void *private;

    // 如下内容放入private
    unsigned long sample_freq;  ///< 采样频率选择，单位hz
    unsigned long adopt_pt;     ///< 采样点数，单位个
    unsigned char ch;
    char	 *otdr_cache;  // 工厂模式下该参数不再偶用，仅为了兼容以前产品，避免再次编译
};

static inline unsigned long get_sample_rate(struct fpga_dev *dev)
{
    return (dev->sample_freq);
}
static inline unsigned long get_sample_num(struct fpga_dev *dev)
{
    return (dev->adopt_pt);
}
static inline unsigned long get_ch(struct fpga_dev *dev)
{
    return (dev->ch);
}
static inline int fpga_cum_count(struct fpga_dev *dev)
{
    return dev->cum_count;
}

#else
#error "-----------------------------------------------"
#error "执行make menuconfig选择工厂模式兼容的结构体 struct fpga_dev，方法gcc *.c -DCONFIG_FACTORY_FOR_TR700"
#error "-----------------------------------------------"
#endif

struct fpga_dev2
{
    int             fd;        ///< 设备FD，正常 fpga_open 后返回
    unsigned long   pid;       ///< 设备id，正常 fpga_open 后返回
    char           *desc;      ///< 设备描述，，正常 fpga_open 后返回
    unsigned long   checksum;  ///< OTDR 曲线数据校验值，fpga_otdr_isfinish返回
    unsigned long   mask_amp;  ///< amp 有效值掩码，fpga_test_param.hw_amp的取值范围
    unsigned long   mask_apd;  ///< apd 有效值掩码，fpga_test_param.hw_apd_vol的取值范围
    pthread_mutex_t mutex;     ///< 互斥锁
    // void *private;

    // 如下内容放入private
    unsigned long sample_freq;  ///< 采样频率选择，单位hz
    unsigned long adopt_pt;     ///< 采样点数，单位个
    unsigned char ch;
};

struct fpga_if
{
    char *type;
    char *name;
    int   a;
    int   b;
    void *private_data;  // BSP 数据
    void *driver_data;   // 驱动malloc数据
    // int (*pfun_fpga_open)(char *device, struct fpga_dev **dev,
    // 		      unsigned long ispeed);
    // int (*pfun_fpga_close)(struct fpga_dev *dev);
    // unsigned int (*pfun_alive)(struct fpga_dev *dev);

    // TODO 开始测试
    int (*pfun_probe)(struct fpga_dev *dev);
    int (*pfun_release)(struct fpga_dev *dev);
    int (*pfun_read_regs)(struct fpga_dev *dev, unsigned int base_addr, unsigned int *regs, int len);
    // 代替以前的OTDR参数配置，虽然FPGA没有实现BASE，但是可以用来区别命令编码
    int (*pfun_write_regs)(struct fpga_dev *dev, unsigned int base_addr, unsigned int *regs, int len);
    int (*pfun_suspend)(struct fpga_dev *dev);
    int (*pfun_resume)(struct fpga_dev *dev);
    int (*pfun_start)(struct fpga_dev *dev);
    int (*pfun_read_frame)(struct fpga_dev *dev, unsigned char *otdr_data, unsigned long len, unsigned long *rlen);
    // TODO 开始查询测试结果
    // 如果你的编译器不支持bool类型，请定义 typedef unsigned char bool代替，
    // 千万不要用unsigned int，否则*val永远返回0
    int (*pfun_isfinish)(struct fpga_dev *dev, bool *val);
    // int (*pfun_isfinish)(struct fpga_dev *dev, int *val);
    // TODO 停止测试
    int (*pfun_stop)(struct fpga_dev *dev);
    void (*pfun_dump)(char *fname, unsigned char *data, unsigned long len);
    int (*pfun_version)(struct fpga_dev *dev, char *soft_ver, char *hw_ver);
    int (*pfun_set_gpio)(struct fpga_dev *dev, int io, int val);
    int (*pfun_get_gpio)(struct fpga_dev *dev, int io, int *val);
    int (*pfun_workthread)(void *ptr);
    // TODO 数据交换高低字节
};
static inline void *fpga_if_get_bsp_data(struct fpga_if *interface)
{
    return interface->private_data;
}

static inline void *fpga_if_get_driver_data(struct fpga_if *interface)
{
    return interface->driver_data;
}

static inline void fpga_if_set_driver_data(struct fpga_if *interface, void *data)
{
    interface->driver_data = data;
}

// #define dev->interface->pfun_isfinish


// 小端
#if 1
// 原来使用这串宏，用在arm上没问题，用在mips数据错误
//#define _STREAM_TO_32(data) (((data)[0] << 0) | ((data)[1] << 8) | ((data)[2]
//<< 16) | ((data)[3] << 24)) #define _STREAM_TO_16(data) (((data)[0] << 0) |
//((data)[1] << 8))

// mips
#define _STREAM_TO_32(data)                                   \
    (0xffffffff &                                             \
     ((((data)[0] & 0xff) << 0) | (((data)[1] & 0xff) << 8) | \
      (((data)[2] & 0xff) << 16) | (((data)[3] & 0xff) << 24)))
#define _STREAM_TO_16(data) \
    (0xffff & ((((data)[0] & 0xff) << 0) | (((data)[1] & 0xff) << 8)))

// #define _STREAM_TO_32(data)                                   \
// 	(((((data)[0] & 0xff) << 0) | (((data)[1] & 0xff) << 8) | \
// 	  (((data)[2] & 0xff) << 16) | (((data)[3] & 0xff) << 24)))
// #define _STREAM_TO_16(data) \
// 	(((((data)[0] & 0xff) << 0) | (((data)[1] & 0xff) << 8)))
#else
#endif

#if 0  // 不方便调试，地址对齐很诡异，不用了，换 __attribute__((constructor(1)))
struct section_type 
{
    char *type;
    char *name;
    void *addr;  // 加上它就不对齐
};

#define SECTION_FPGA_IF(valname, strname, paddr)             \
    volatile struct section_type __fpga_if_##valname         \
	    __attribute__((unused, section(".fpga_if"))) = { \
		.type = MAGIC_FPGA_IF,                       \
		.name = (char *)#strname,                    \
	    }
#endif

struct fpga_info
{
    int hw_ver;    ///< 硬件版本
    int ch;        ///< 通道数
    int soft_ver;  ///< 软件版本
};

//*****************************************************************************
// FPGA所有项目用到的命令编号
//*****************************************************************************

/* OTDR 测试数据命令 */
#define OTDR_RETURN (0xf0)  // 无用
#define OTDR_RESET  (0xe0)  // 无用

#define OTDR_PARAM  (0x10)  // OTDR 设置参数
#define OTDR_START  (0x11)  // 启动OTDR测试
#define OTDR_STOP   (0x12)  // 停止OTDR测试
#define OTDR_IS_FINISH \
    (0x13)  // 询问OTDR测试是否完成，这个东西最好作废，改成中断

#define OTDR_REDLASER        (0x14)  // 作废：FPGA控制红光

#define OTDR_ALIVE           (0xa0)  // 判断FPGA通信是否正常
#define OTDR_COM_LASER       (0x15)  // 是否存在通信光
#define OTDR_VER             (0xa1)  // 获取FPGA版本
#define OTDR_ROTDR           (0x20)  // 返回部分OTDR数据，数据长度根据具体型号而定
#define OTDR_APD_TEMP        (0x21)  // 读取APD温度，温度来源于FPGA
#define OTDR_POWER           (0x22)  // 作废：OTDR电源控制
#define OTDR_PULSE_ENCODE    (0x23)  // 作废：OTDR脉冲模式

/* OCVL 测试数据命令 */
#define OCVL_PARAM           (0x16)  // 震动定位

//#define OTDR_RD_REG             (0x24)  // TF500 对寄存器芯片进行读操作

#define OTDR_ALARM_APPEAR    (0x25)  // TMS400 告警输出 数码管显示
#define OTDR_ALARM_DISAPPEAR (0x26)  // TMS400 告警取消

#define OTDR_WD_REG          (0x27)  // TF500 对寄存器芯片进行写操作 2018-8-27
#define OTDR_RD_REG          (0x28)  // TF500 对寄存器芯片进行读操作 2018-8-27

#define OCID_PARAM           (0x30)
#define OCID_LEVEL           (0x31)
#define OCID_START           (0x32)
#define OCID_STOP            OTDR_STOP
#define OCID_READ_DATA       (0x33)

#define OCID_PARAM_LOSS      (0x35)
#define ROUTE_DETECT         (0x34)  // 路由探测，无参数
#define CMD_GPIO             (0X22)
#define CMD_MDIO             (0X23)


#define MIN_TX_PACK_SIZE     (12)  // 最小发送帧长度
struct fpga_2016
{
    char head[5];
    /*
    FPGA 里忽略 glink 与 cmd 之间的 4 byte
    */

#if 1
    char len;
    char unuse0;
#else
    short len;
#endif
    char unuse1;
    char addr;
    char cmd;
} __attribute__((__packed__));


struct fpga_test_param
{
    /*!<
        OTDR 测试参数描述，与硬件无关

           <      1 / laser_freq   >
           <  pulse >
            --------                 --------
           |        |               |        |
           |        |               |        |
           |        |               |        |
        ---|        |---------------|        |------------

            ^ ^ ^ ^ ^ ^ ^ ^          ^ ^ ^ ^ ^ ^ ^ ^
            | | | | | | | |          | | | | | | | |
               /   \
              /     \
            /         \
              /             \
             <1 / sample_freq>
    */
    bool          en_hw_dist;
    unsigned long dist;  /// 测试量程单位米，该字段内容决定采用什么采样率
                         /// fpga_test_param.sample_freq

    unsigned short laser_freq;  ///< 激光器脉冲频率选择
    bool           en_hw_pulse;
    unsigned short pulse; /*!< 脉冲宽度，单位ns*/


    unsigned short sample_freq;  ///< 采样频率选择，单位hz

    bool           en_hw_adopt_pt;
    unsigned long  adopt_pt;  ///< 采样点数，单位个
    bool           en_hw_add_cnt;
    unsigned short add_cnt;  ///< 累加次数，单位次
    bool           en_hw_add_ms;
    unsigned short add_ms;  ///< 累加时间，单位ms

    unsigned long amp; /*!< 使能放大： 1：使能放大开关，与具体放大倍数无关.
               但必须保证，各放大开关打开之后效果必定比前一个大\n

               运放级别分成3级，对应24bit \n
                00000000 00000000 00000000 00000000\n
               |..........................|=======| ----  1级放大\n
               |.................|=======|......... ----  2级放大\n
               |........|=======|.................. ----  3级放大\n
               |=======|...........................  ----  预留\n
                例子:\n\n
                00000000 00000000 00000011 00000001\n --->
               使能1级放大开关S1-1，使能2级放大S2-1、S2-2
               */
    bool en_hw_amp;
    char hw_amp;

    char apd_vol; /*!< APD电压，数值越高电压越高，@ref APD电压范围
              OTDR_APD_LOW~OTDR_APD_HIGH
              */
    bool en_hw_apd_vol;
    char hw_apd_vol;

    char power; /*!< 激光器功率，数值越高功率越高，@ref 光功率控制
            OTDR_LASER_PW_LOW ~ OTDR_LASER_PW_HIGH
            */

    char isrun;  ///< 是否启动


    short laser_ch; /*!< 激光器通道，从1开始计数，
            同时 “可能选择性” 支持以波长（单位nm）方式指定通道，
            如：1310、1550、1625。\n
            1 通道对应 1310nm 激光器\n
            2 通道对应 1550nm 激光器\n
            具体对应关系与接口实现有关
            */
    char panel_ch;  /*!< 面板光口通道，从 1 开始计数
            可能由光开关控制，同一面板通道可能不同时刻用于不同激光器通道
            */
#if defined(CONFIG_EN_CAP)
    unsigned long cap;        //  电容等级，描述值
    bool          en_hw_cap;  //  使能硬件编码
    char          hw_cap;     //  硬件编码
#endif
};

extern int  env_otdr_dbg_level;
extern int  env_print_otdr_data;
extern int  env_dbg_tx;
extern int  env_dbg_rx;
extern int  env_dbg_bit_otdr_data;
extern int  env_print_e;
extern int  env_print_w;
extern int  env_lowpower_thread;
extern int  env_unuse_cksum;
extern int  env_speed;
extern char env_dev_led_run[128];


#define ACK_CMD_OFFSET   (9)
#define ACK_CODE_OFFSET  (10)
#define ACK_CMD(x)       (*((x) + ACK_CMD_OFFSET))
#define ACK_CODE(x)      (*((x) + ACK_CODE_OFFSET))

// TODO 是不是要用回调机制，修改缓存大小
// #define LEN_OTDR_CACHE (32000 * 4 + 4000)
#define LEN_OTDR_CACHE   (64000 * 4 + 4000)

#define CHKSUM_OFFSET    (14)
#define CUM_COUNT_OFFSET (12)


#define _1MS             (1000)

#define DATA_FLAG        0x23  // On TR700
// #define DATA_FLAG 0x22 // On Y2 ,only for debug

#define ARRAY_SIZE(a)    (sizeof(a) / sizeof((a)[0]))

#define OTDR_DBG_LEVEL   (1)


#if 1
extern int watchdog;
#define TIMEOUT_S        (15)
#define WDG_IS_TIMEOUT() (watchdog > TIMEOUT_S)
static void inline wdt_clear()
{
    watchdog = 0;
}

static void inline wdt_tick()
{
    watchdog++;
}
#else
#define wdt_clear()
#define wdt_tick()
#endif


#define DEFAULT_FPGA_IF                                             \
    .name               = "this-unname", /* 防止忘记命名 */   \
	    .pfun_probe = none_probe, .pfun_release = none_release, \
    .pfun_suspend = none_suspend, .pfun_resume = none_resume,       \
    .pfun_start      = none_start,                                  \
    .pfun_read_frame = none_read_frame,                             \
    .pfun_read_regs  = none_read_regs,                              \
    .pfun_write_regs = none_write_regs,                             \
    .pfun_isfinish = none_isfinish, .pfun_stop = none_stop,         \
    .pfun_dump = none_dump, .pfun_version = none_version,           \
    .pfun_set_gpio = none_set_gpio, .pfun_get_gpio = none_get_gpio

typedef unsigned char uint8_t;
typedef unsigned int  uint32_t;

// =====================================================
// 这部分API应该会整理
void         PrintfMemory(uint8_t *buf, uint32_t len);
unsigned int _read_otdr_1(struct fpga_dev *dev, unsigned char *buf, int size);
void         memcpy_char(char *dst, char *src, int len);
// void           brief_print_data(unsigned long *buf, int len);
void           brief_print_data(unsigned int *buf, int len);
unsigned short _tx_checksum(uint8_t *data, int len);
void           _show_cmd_name(unsigned char cmd);
// =====================================================

//=======================================================
// core API
int             fpga_if_register(struct fpga_if *interface);
int             fpga_if_unregister(char *name);
struct fpga_if *fpga_if_append(char *name);
void            fpga_if_check(struct fpga_if *interface);

// core 缺省无用API

int  none_probe(struct fpga_dev *dev);
int  none_release(struct fpga_dev *dev);
int  none_suspend(struct fpga_dev *dev);
int  none_resume(struct fpga_dev *dev);
int  none_start(struct fpga_dev *dev);
int  none_read_frame(struct fpga_dev *dev, unsigned char *otdr_data, unsigned long len, unsigned long *rlen);
int  none_isfinish(struct fpga_dev *dev, bool *val);
int  none_stop(struct fpga_dev *dev);
void none_dump(char *fname, unsigned char *data, unsigned long len);
int  none_version(struct fpga_dev *dev, char *soft_ver, char *hw_ver);
int  none_read_regs(struct fpga_dev *dev, unsigned int base_addr, unsigned int *regs, int len);
int  none_write_regs(struct fpga_dev *dev, unsigned int base_addr, unsigned int *regs, int len);
int  none_set_gpio(struct fpga_dev *dev, int io, int val);
int  none_get_gpio(struct fpga_dev *dev, int io, int *val);

//=======================================================
// 接口API
extern void         fpga_show_env();
extern int          fpga_power_up();
extern int          fpga_open(char *device, struct fpga_dev **dev, unsigned long ispeed);
extern int          fpga_open_interface(char *device, struct fpga_dev **dev, unsigned long ispeed, char *ifname);
extern int          fpga_close(struct fpga_dev *dev);
extern unsigned int com_fpga_alive(struct fpga_dev *dev);
extern int          fpga_read_otdr_data(struct fpga_dev *dev, unsigned char *otdr_data, unsigned long len);
extern void         com_fpga_save_32bit(char *fname, unsigned char *data, unsigned long len);
/*
 * com_fpga_info包含比com_fpga_version更多得信息
 * 最初只需要version，后来为了得到硬件信息，在OTDR_VER命令返回内容携带FPGA可支持得通道数
 */
extern int com_fpga_info(struct fpga_dev *dev, struct fpga_info *out);
extern int com_fpga_version(struct fpga_dev *dev, char *soft_ver, char *hw_ver);


unsigned int _read_otdr_serial(struct fpga_dev *dev, unsigned char *buf, unsigned int byte, unsigned int delay);
// int _otdr_data_swap(unsigned char *buf, int len);
void          _print_otdr_data(unsigned long *buf, int len);
unsigned long _otdr_checksum(unsigned char *data, unsigned long len);
//=======================================================
// 公共API
int fpga_send_data(struct fpga_dev *dev, char cmd, void *t_buf, int tlen, void *r_buf, int rlen);


//=======================================================
// 简化指针
// TODO 这部分不想考虑效率，不用内联，避免以后修改结构体引起问题
static inline int fpga_if_probe(struct fpga_dev *dev)
{
    return dev->interface->pfun_probe(dev);
}
static inline int fpga_if_release(struct fpga_dev *dev)
{
    return dev->interface->pfun_release(dev);
}
static inline int fpga_if_suspend(struct fpga_dev *dev)
{
    return dev->interface->pfun_suspend(dev);
}
static inline int fpga_if_resume(struct fpga_dev *dev)
{
    return dev->interface->pfun_resume(dev);
}
static inline int fpga_if_start(struct fpga_dev *dev)
{
    return dev->interface->pfun_start(dev);
}
static inline int fpga_if_isfinish(struct fpga_dev *dev, bool *val)
{
    return dev->interface->pfun_isfinish(dev, val);
}

static inline int fpga_if_read_frame(struct fpga_dev *dev,
                                     unsigned char   *otdr_data,
                                     unsigned long    len,
                                     unsigned long   *rlen)
{
    return dev->interface->pfun_read_frame(dev, otdr_data, len, rlen);
}

static inline int fpga_if_stop(struct fpga_dev *dev)
{
    return dev->interface->pfun_stop(dev);
}
static inline void fpga_if_dump(struct fpga_dev *dev, char *fname, unsigned char *data, unsigned long len)
{
    return dev->interface->pfun_dump(fname, data, len);
}
static inline int fpga_if_version(struct fpga_dev *dev, char *soft_ver, char *hw_ver)
{
    return dev->interface->pfun_version(dev, soft_ver, hw_ver);
}
static inline int _fpga_if_set_gpio(struct fpga_dev *dev, int io, int val)
{
    return dev->interface->pfun_set_gpio(dev, io, val);
}
static inline int _fpga_if_get_gpio(struct fpga_dev *dev, int io, int *val)
{
    return dev->interface->pfun_get_gpio(dev, io, val);
}
int        fpga_if_get_gpio(struct fpga_dev *dev, int io, int *val);
extern int fpga_if_get_gpio(struct fpga_dev *dev, int io, int *val);

static inline int fpga_if_read_regs(struct fpga_dev *dev, unsigned int base_addr, unsigned int *regs, int len)
{
    return dev->interface->pfun_read_regs(dev, base_addr, regs, len);
}

static inline int _fpga_if_wrirte_regs(struct fpga_dev *dev, unsigned int base_addr, unsigned int *regs, int len)
{
    return dev->interface->pfun_write_regs(dev, base_addr, regs, len);
}
void fpga_thread(struct fpga_dev *dev);

void fpga_show_env(void);
void fpga_update_env(void);
#include "interface/protocal-ocid.h"
#include "interface/protocal-otdr.h"

#endif
